home *** CD-ROM | disk | FTP | other *** search
- RCS_ID_C="$Id: kern_synch.c,v 1.25 1994/04/02 10:29:54 jraja Exp $";
- /*
- * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
- * Helsinki University of Technology, Finland.
- * All rights reserved.
- *
- * HISTORY
- * $Log: kern_synch.c,v $
- * Revision 1.25 1994/04/02 10:29:54 jraja
- * Changed "wmesg" to const.
- *
- * Revision 1.24 1993/10/07 22:43:02 ppessi
- * Changed AbortIO/Remove to AbortIO/WaitIO
- *
- * Revision 1.23 1993/06/04 11:16:15 jraja
- * Fixes for first public release.
- *
- * Revision 1.22 1993/06/03 20:16:47 too
- * Changed checking of sigIntrMask so that signals both in wakemask and
- * sigIntrMask does not return EINTR but ERESTART
- *
- * Revision 1.21 93/06/03 00:52:51 00:52:51 jraja (Jarno Tapio Rajahalme)
- * Changed the order in which tsleep_main() checks for events: EINTR is tested
- * first, then ERESTART (user signals), then wakeup and finally time out.
- *
- * Revision 1.20 1993/05/24 19:30:25 ppessi
- * Changed signal handling mechanism in tsleep_main() and WaitSelect()
- *
- * Revision 1.19 1993/05/17 01:07:47 ppessi
- * Changed RCS version.
- *
- * Revision 1.18 1993/04/26 11:54:43 too
- * Changed include paths of amiga_api.h, amiga_libcallentry.h and amiga_raf.h
- * from kern to api
- *
- * Revision 1.17 93/04/20 16:08:03 16:08:03 jraja (Jarno Tapio Rajahalme)
- * The timer request is now ensured to be referenced by the p->tsleep_timer
- * only in the send_timeout.
- *
- * Revision 1.16 93/04/19 02:23:48 02:23:48 ppessi (Pekka Pessi)
- * Fixed various bugs with tsleep_main().
- * Timeouts and SIGINTR mask seem to work.
- *
- * Revision 1.15 93/04/13 22:30:59 22:30:59 jraja (Jarno Tapio Rajahalme)
- * Added diagnostic to tsleep() to test that the caller has the
- * syscall_semaphore.
- *
- * Revision 1.14 93/04/12 00:00:53 00:00:53 jraja (Jarno Tapio Rajahalme)
- * Changed wakeup to use same signal with the timer.
- * Added ULONG *sigmp argument to tsleep_main().
- *
- * Revision 1.13 93/04/06 15:15:52 15:15:52 jraja (Jarno Tapio Rajahalme)
- * Changed spl function return value storage to spl_t,
- * changed bcopys and bzeros to aligned and/or const when possible,
- * added inclusion of conf.h to every .c file.
- *
- * Revision 1.12 93/04/05 14:57:45 14:57:45 jraja (Jarno Tapio Rajahalme)
- * Added more efficient spl functions when not debugging.
- *
- * Revision 1.11 93/03/19 14:14:49 14:14:49 too (Tomi Ollila)
- * Code changes at night 17-18 March 1993
- *
- * Revision 1.10 93/03/17 12:07:51 12:07:51 jraja (Jarno Tapio Rajahalme)
- * Added more diagnostic to the tsleep().
- * Moved all the work from tsleep_abort_timeout to tsleep_send_timeout.
- *
- * Revision 1.9 93/03/16 19:45:22 19:45:22 too (Tomi Ollila)
- * Code kludges...(Added zero timeout -> no timeout)
- *
- * Revision 1.8 93/03/11 20:49:14 20:49:14 jraja (Jarno Tapio Rajahalme)
- * Changed the splsemaphore to spl_semaphore.
- *
- * Revision 1.7 93/03/07 00:55:23 00:55:23 jraja (Jarno Tapio Rajahalme)
- * Removed redundant assignment of the Wait() result.
- *
- * Revision 1.6 93/03/05 03:26:16 03:26:16 ppessi (Pekka Pessi)
- * Compiles with SASC. Initial test version.
- *
- * Revision 1.5 93/02/25 18:39:23 18:39:23 jraja (Jarno Tapio Rajahalme)
- * Moved amiga_includes little upper.
- *
- * Revision 1.4 93/02/24 12:54:51 12:54:51 jraja (Jarno Tapio Rajahalme)
- * Changed init to remember if initialized.
- *
- * Revision 1.3 93/02/17 12:54:16 12:54:16 jraja (Jarno Tapio Rajahalme)
- * Broke tsleep apart: tsleep_send_timeout(), tsleep_enter(), tsleep_main()
- * and tsleep_abort_timeout(). See tsleep() for usage.
- *
- * Revision 1.2 93/02/04 18:28:22 18:28:22 jraja (Jarno Tapio Rajahalme)
- * Added sleep_init(), tsleep() and wakeup().
- *
- * Revision 1.1 92/12/22 00:08:12 00:08:12 jraja (Jarno Tapio Rajahalme)
- * Initial revision
- */
-
- #include <conf.h>
-
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/synch.h>
- #include <sys/queue.h>
- #include <sys/syslog.h>
-
- #include <kern/amiga_includes.h>
-
- #include <api/amiga_api.h>
-
- /*
- * Note about spl-functions: this implementation does NOT check for software
- * interrupts when returning to level spl 0 (not needed on AmigaOS, see the
- * bottom of this file).
- */
-
- /*
- * Sleeping threads are hashed by 'chan' onto sleep queues.
- */
-
- #define SLEEP_QUEUE_SIZE 32 /* power of 2 */
- #define SLEEP_HASH(x) (((int)(x)>>5) & (SLEEP_QUEUE_SIZE - 1))
-
- queue_head_t sleep_queue[SLEEP_QUEUE_SIZE];
-
- /*
- * semaphore protecting sleep queues
- */
- struct SignalSemaphore sleep_semaphore = { 0 };
- static BOOL sleep_initialized = FALSE;
-
- /*
- * Sleep system initialization.
- */
- BOOL
- sleep_init(void)
- {
- register int i;
-
- if (!sleep_initialized) {
- /*
- * initialize the semaphore protecting sleep queues
- */
- InitSemaphore(&sleep_semaphore);
-
- /*
- * initialize the sleep queues
- */
- for (i = 0; i < SLEEP_QUEUE_SIZE; i++)
- queue_init(&sleep_queue[i]);
-
- sleep_initialized = TRUE;
- }
- return TRUE;
- }
-
- void
- tsleep_send_timeout(struct SocketBase *p,
- const struct timeval *time_out)
- {
-
- /*
- * Make sure that the timer message is back from the timer device
- */
- if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_UNKNOWN) {
- /*
- * abort previous timeout if it has not been completed yet
- */
- if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_REPLYMSG) {
- AbortIO((struct IORequest *)(p->tsleep_timer));
- }
- /*
- * Remove timerequest from reply port.
- */
- WaitIO((struct IORequest *)p->tsleep_timer);
- /*
- * Set the node type to NT_UNKNOWN to mark that it is referenced only by
- * the p->tsleep_timer.
- */
- p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
- #if 0
- /*
- * Make sure the signal gets cleared.
- *
- * if this is not done, the tsleep_main will do one unnecessary round
- * IF the signal was set. IS THIS WORTH OF IT?
- */
- SetSignal(0, 1 << p->timerPort->mp_SigBit);
- #endif
- }
- /*
- * send timeout request if necessary
- */
- if (time_out) {
- /*
- * set the timeout
- */
- p->tsleep_timer->tr_time = *time_out;
- /*
- * Enable signalling again
- */
- p->timerPort->mp_Flags = PA_SIGNAL;
- /*
- * send the request to the timer device
- */
- BeginIO((struct IORequest *)(p->tsleep_timer));
- }
- }
-
- void
- tsleep_abort_timeout(struct SocketBase *p,
- const struct timeval *time_out)
- {
- if (time_out) {
- /*
- * do not signal us any more
- */
- p->timerPort->mp_Flags = PA_IGNORE;
- }
- }
-
- void
- tsleep_enter(struct SocketBase *p,
- caddr_t chan, /* 'channel' to wait on */
- const char *wmesg) /* reason to sleep */
- {
- register queue_t q;
-
- /*
- * Zero is a reserved value, used to indicate
- * that we have been woken up and are no longer on
- * the sleep queues.
- */
-
- #if DIAGNOSTIC
- if (chan == 0)
- panic("tsleep");
- #endif
-
- /*
- * The sleep_semaphore protects the sleep queues and
- * p_ fields in SocketBases.
- *
- * When the process is in a sleep queue its p_wchan field is nonzero.
- */
- ObtainSemaphore(&sleep_semaphore);
- p->p_wchan = chan;
- p->p_wmesg = wmesg;
- q = &sleep_queue[SLEEP_HASH(chan)];
- queue_enter(q, p, struct SocketBase *, p_sleep_link);
- ReleaseSemaphore(&sleep_semaphore);
- }
-
- int
- tsleep_main(struct SocketBase *p, ULONG wakemask)
- {
- ULONG sigmask, bmask, timermask;
- struct timerequest *timerReply;
- register queue_t q;
- int result;
-
- /*
- * Set the signal mask for the wait
- */
- timermask = 1 << p->timerPort->mp_SigBit;
- sigmask = timermask | p->sigIntrMask | wakemask;
-
- for (;;) {
- /*
- * wait for timeout, wakeup or interrupt
- */
- bmask = Wait(sigmask);
-
- /*
- * Check if we were interrupted
- */
- if (bmask & p->sigIntrMask & ~wakemask) {
- result = EINTR;
- break;
- }
-
- /*
- * Check for user signals
- */
- if (bmask & wakemask) {
- result = ERESTART;
- break;
- }
-
- /*
- * check if we were woken up.
- *
- * If p->p_chan is zero then the wakener has removed us from
- * the sleep queue.
- */
- if (p->p_wchan == 0) {
- /*
- * Set back the signals which interrupted us so that user program can
- * detect them
- */
- bmask &= p->sigIntrMask|wakemask;
- if (bmask)
- SetSignal(bmask, bmask);
-
- return 0; /* return success */
- }
-
- /*
- * check if we got the timer reply signal and message
- */
- if (bmask & timermask &&
- (timerReply = (struct timerequest *)GetMsg(p->timerPort)) &&
- timerReply == p->tsleep_timer) { /* sanity check */
- /*
- * timeout expired.
- *
- * Set the node type to NT_UNKNOWN to mark that it is referenced only by
- * the p->tsleep_timer.
- */
- timerReply->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
-
- result = EWOULDBLOCK;
- break;
- }
-
- } /* for */
-
- /* Return path when sleeper has to be removed from the sleep queue */
-
- /*
- * Set back the signals which interrupted us so that user program can
- * detect them
- */
- bmask &= p->sigIntrMask | wakemask;
- if (bmask)
- SetSignal(bmask, bmask);
-
- /*
- * remove from the sleep queue
- */
- ObtainSemaphore(&sleep_semaphore);
- /*
- * If p_chan is nonzero then we still are on the sleep queue and
- * need to be removed from there.
- */
- if (p->p_wchan != 0) {
- q = &sleep_queue[SLEEP_HASH(p->p_wchan)];
- p->p_wchan = (char *)0;
- queue_remove(q, p, struct SocketBase *, p_sleep_link);
- }
- ReleaseSemaphore(&sleep_semaphore);
-
- return result;
- }
-
- /*
- * General sleep call.
- * NOTE: caller is assumed to hold the syscall_semaphore! \* XXX *\
- * Suspends current process until a wakeup is made on chan.
- * Sleeps at most the time specified in a time_out (NULL means no timeout).
- * Lowers the current spl-level to 0 while in sleep.
- * Returns 0 if awakened, EWOULDBLOCK if the timeout expires and
- * EINTR if interrupted.
- */
- int
- tsleep(struct SocketBase *p, /* Library base through which this call came */
- caddr_t chan, /* 'channel' to wait on */
- const char *wmesg, /* reason to sleep */
- const struct timeval *time_out) /* timeout as timeval structure */
- {
- int result;
- spl_t old_spl;
-
- #if DIAGNOSTIC
- extern struct Task *AmiTCP_Task;
- if (FindTask(NULL) == AmiTCP_Task) {
- log(LOG_ERR, "AmiTCP did tsleep() itself!");
- return (-1);
- }
- #endif
-
- #if DIAGNOSTIC
- if (p == NULL) {
- log(LOG_ERR, "tsleep() called with NULL SocketBase pointer!");
- return (-1);
- }
- #endif
-
- #if DIAGNOSTIC
- if (SysBase->ThisTask != syscall_semaphore.ss_Owner) {
- log(LOG_ERR, "tsleep() called with NO syscall_semaphore!");
- return (-1);
- }
- #endif
-
- tsleep_send_timeout(p, time_out);
-
- tsleep_enter(p, chan, wmesg);
-
- /*
- * release spl-level while in sleep.
- *
- * NOTE: syscall_semaphore must be freed as well!
- */
-
- old_spl = spl0();
- ReleaseSemaphore(&syscall_semaphore); /* XXX */
-
- result = tsleep_main(p, 0);
-
- /*
- * return old spl-level
- */
- ObtainSemaphore(&syscall_semaphore); /* XXX */
- splx(old_spl);
-
- /*
- * abort the timeout request if necessary
- */
- if (result != EWOULDBLOCK)
- tsleep_abort_timeout(p, time_out);
-
- return (result);
- }
-
- void
- wakeup(caddr_t chan)
- {
- register queue_t q;
- struct SocketBase *p, *next;
-
- #if DIAGNOSTIC
- if (chan == 0) {
- log(LOG_ERR, "wakeup on chan zero");
- return;
- }
- #endif
-
- ObtainSemaphore(&sleep_semaphore);
- q = &sleep_queue[SLEEP_HASH(chan)];
-
- p = (struct SocketBase *)queue_first(q);
- while (!queue_end(q, (queue_entry_t)p)) {
- next = (struct SocketBase *)queue_next(&p->p_sleep_link);
- if (p->p_wchan == chan) {
- /*
- * mark sleeper as woken up
- */
- p->p_wchan = NULL;
- /*
- * remove sleeper from the sleep queue
- */
- queue_remove(q, p, struct SocketBase *, p_sleep_link);
- /*
- * signal process to take attention
- */
- Signal(p->thisTask, 1 << p->timerPort->mp_SigBit);
- }
- p = next;
- }
- ReleaseSemaphore(&sleep_semaphore);
- }
-
- /*
- * Spl-level emulation:
- *
- * In this implementation the processor priority levels are modelled
- * either with one semaphore (ifdef DEBUG) or by Exec Task switch
- * disabling feature. Semaphore is used while debugging for security (to
- * be able to single step almost everywhere). The production version uses
- * ExecBase's TDNestCnt for speed (prevent unnecessary task switches).
- *
- * Note that both ways lead to the fact that when someone sets, say, splimp()
- * he will WAIT for the holder of splnet() to finish.
- */
-
- #ifdef DEBUG
- /*
- * spl_semaphore is used as mutex for all spl-levels
- *
- * Note that InitSemaphore() requires the signalSemaphore to be initialized
- * to zero (here done statically).
- */
- struct SignalSemaphore spl_semaphore = { 0 };
-
- /*
- * spl_level holds the current pseudo priority level.
- * NOTE: this may be accessed only while holding the spl_semaphore.
- */
- spl_t spl_level = SPL0;
- static BOOL spl_initialized = FALSE;
-
- BOOL
- spl_init(void)
- {
- if (!spl_initialized) {
- /*
- * Initialize spl_semaphore for use. After this call any number of
- * tasks may use spl-functions.
- */
- InitSemaphore(&spl_semaphore);
- spl_initialized = TRUE;
- }
- return TRUE;
- }
-
- spl_t
- spl_n(spl_t new_level)
- {
- register spl_t old_level;
-
- ObtainSemaphore(&spl_semaphore);
- old_level = spl_level;
- spl_level = new_level;
-
- if (new_level > old_level) { /* raise level */
- if (old_level == 0) /* lock when raising over zero */
- /*
- * so. return without releasing the lock
- */
- return old_level;
- }
- else if (new_level < old_level) { /* lower level */
- if (new_level == 0) /* unlock when lowering to zero */
- /*
- * now release the lock kept above
- */
- ReleaseSemaphore(&spl_semaphore);
- }
- ReleaseSemaphore(&spl_semaphore);
-
- return old_level;
- }
-
- #else
-
- BOOL
- spl_init(void)
- {
- return TRUE;
- }
-
- /*
- * spl_n is defined as an inline in <sys/synch.h>
- */
- #endif /* DEBUG */
-